// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 复制自Go发行版src/Go/build/read.Go。

package imports

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"unicode/utf8"
)

type importReader struct {
	b    *bufio.Reader
	buf  []byte
	peek byte
	err  error
	eof  bool
	nerr int
}

var bom = []byte{0xef, 0xbb, 0xbf}

func newImportReader(b *bufio.Reader) *importReader {
	// 移除前导UTF-8 BOM表。
	// 根据https：
	// 编译器可以忽略UTF-8编码的字节顺序标记（U+FEFF）
	// 如果它是源文本中的第一个Unicode代码点。
	if leadingBytes, err := b.Peek(3); err == nil && bytes.Equal(leadingBytes, bom) {
		b.Discard(3)
	}
	return &importReader{b: b}
}

func isIdent(c byte) bool {
	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
}

var (
	errSyntax = errors.New("syntax error")
	errNUL    = errors.New("unexpected NUL in input")
)

// syntaxError记录语法错误，但仅当尚未记录I/O错误时。
func (r *importReader) syntaxError() {
	if r.err == nil {
		r.err = errSyntax
	}
}

// readByte从输入中读取下一个字节，将其保存在buf中，然后返回。
// 如果发生错误，readByte会在r.err中记录错误并返回0。
func (r *importReader) readByte() byte {
	c, err := r.b.ReadByte()
	if err == nil {
		r.buf = append(r.buf, c)
		if c == 0 {
			err = errNUL
		}
	}
	if err != nil {
		if err == io.EOF {
			r.eof = true
		} else if r.err == nil {
			r.err = err
		}
		c = 0
	}
	return c
}

// peekByte从输入读取器返回下一个字节，但不会超出该字节。
// 如果设置了skipSpace，peekByte将跳过前导空格和注释。
func (r *importReader) peekByte(skipSpace bool) byte {
	if r.err != nil {
		if r.nerr++; r.nerr > 10000 {
			panic("go/build: import reader looping")
		}
		return 0
	}

	// 使用r.peek作为第一个输入字节。
	// 不要只在这里返回r.peek：它可能是peekByte留下的（false）
	// 这可能是peek字节（true）。
	c := r.peek
	if c == 0 {
		c = r.readByte()
	}
	for r.err == nil && !r.eof {
		if skipSpace {
			// 对于本读者来说，分号是不必要的
			// 理解输入并将其视为空格。
			switch c {
			case ' ', '\f', '\t', '\r', '\n', ';':
				c = r.readByte()
				continue

			case '/':
				c = r.readByte()
				if c == '/' {
					for c != '\n' && r.err == nil && !r.eof {
						c = r.readByte()
					}
				} else if c == '*' {
					var c1 byte
					for (c != '*' || c1 != '/') && r.err == nil {
						if r.eof {
							r.syntaxError()
						}
						c, c1 = c1, r.readByte()
					}
				} else {
					r.syntaxError()
				}
				c = r.readByte()
				continue
			}
		}
		break
	}
	r.peek = c
	return r.peek
}

// nextByte类似于peekByte，但超出了返回的字节。
func (r *importReader) nextByte(skipSpace bool) byte {
	c := r.peekByte(skipSpace)
	r.peek = 0
	return c
}

// readKeyword从输入中读取给定的关键字。
// 如果关键字不存在，readKeyword会记录语法错误。
func (r *importReader) readKeyword(kw string) {
	r.peekByte(true)
	for i := 0; i < len(kw); i++ {
		if r.nextByte(false) != kw[i] {
			r.syntaxError()
			return
		}
	}
	if isIdent(r.peekByte(false)) {
		r.syntaxError()
	}
}

// ReadIdentit从输入中读取标识符。
// 如果标识符不存在，ReadIdentit将记录语法错误。
func (r *importReader) readIdent() {
	c := r.peekByte(true)
	if !isIdent(c) {
		r.syntaxError()
		return
	}
	for isIdent(r.peekByte(false)) {
		r.peek = 0
	}
}

// readString从输入中读取带引号的字符串文字。
// 如果标识符不存在，readString将记录语法错误。
func (r *importReader) readString(save *[]string) {
	switch r.nextByte(true) {
	case '`':
		start := len(r.buf) - 1
		for r.err == nil {
			if r.nextByte(false) == '`' {
				if save != nil {
					*save = append(*save, string(r.buf[start:]))
				}
				break
			}
			if r.eof {
				r.syntaxError()
			}
		}
	case '"':
		start := len(r.buf) - 1
		for r.err == nil {
			c := r.nextByte(false)
			if c == '"' {
				if save != nil {
					*save = append(*save, string(r.buf[start:]))
				}
				break
			}
			if r.eof || c == '\n' {
				r.syntaxError()
			}
			if c == '\\' {
				r.nextByte(false)
			}
		}
	default:
		r.syntaxError()
	}
}

// readImport读取导入子句-可选标识符，后跟带引号的字符串-
// 从输入。
func (r *importReader) readImport(imports *[]string) {
	c := r.peekByte(true)
	if c == '.' {
		r.peek = 0
	} else if isIdent(c) {
		r.readIdent()
	}
	r.readString(imports)
}

// ReadComments与io.ReadAll类似，只是它只读取前导
// 文件中的注释块。
func ReadComments(f io.Reader) ([]byte, error) {
	r := newImportReader(bufio.NewReader(f))
	r.peekByte(true)
	if r.err == nil && !r.eof {
		// 未达到EOF，因此必须找到非空格字节。移除它。
		r.buf = r.buf[:len(r.buf)-1]
	}
	return r.buf, r.err
}

// ReadImports与io.ReadAll类似，只是它需要一个Go文件作为输入
// 并在导入完成后停止读取输入。
func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
	r := newImportReader(bufio.NewReader(f))

	r.readKeyword("package")
	r.readIdent()
	for r.peekByte(true) == 'i' {
		r.readKeyword("import")
		if r.peekByte(true) == '(' {
			r.nextByte(false)
			for r.peekByte(true) != ')' && r.err == nil {
				r.readImport(imports)
			}
			r.nextByte(false)
		} else {
			r.readImport(imports)
		}
	}

	// 如果我们在EOF之前成功停止，我们会读取一个字节，告诉我们我们已经完成了。
	// 返回除最后一个字节以外的所有字节，如果让它通过，将导致语法错误。
	if r.err == nil && !r.eof {
		return r.buf[:len(r.buf)-1], nil
	}

	// 如果由于语法错误而停止，请使用整个文件，以便
	// 我们确信不会更改go/parser返回的错误。
	if r.err == errSyntax && !reportSyntaxError {
		r.err = nil
		for r.err == nil && !r.eof {
			r.readByte()
		}
	}

	return r.buf, r.err
}
